home *** CD-ROM | disk | FTP | other *** search
- /* Functions for archiving data to a file. A header containing the size
- and the version of the data is appended, so that if the definition
- of the data changes from the version stored on disk it should still be
- possible to use the stored data. If you design your data so that
- the default values of fields are NULL, then you will probably not
- even have to be concerned with the versions of your data. For instance,
- if you append new fields to a data structure, and then read in an old
- version of the same data structure, then the new fields will all be NULL.
- But since these are the default values for the new fields, no extra action
- needs to be taken. If you really must take some action, then
- you can compare the version of the returned data with the version you
- requested and take appropriate action.
-
- Besides automating the problem of different versions of data, the archive
- library could be modified to apply transformations to all of your data which
- are transparent to your program. For instance, a change I am contemplating
- to the archive library would apply a simple and fast compression algorithm
- to the data. Some of the data structures I now archive contain long sequences
- of NULLs, and it would be fairly straighforward to use run-length encoding
- to reduce the size of the data. The archive library would be modified to
- compress the data just before it is written to disk and decompress the data
- just after it is read from disk. All these transformations can be fully
- backwards compatible with older versions of data files since the archive
- library stamps every item of data it writes to disk with a version number;
- so data with a pre-compression version would be left untouched, while
- newer data would be compressed.
-
- Revision History:
-
- 93/12/15 aih
- - removed scrap functions since they aren't being used and are difficult
- to maintain
-
- 91/11/15 AIH
- - Uses min and max functions instead of macros
-
- 91/05/07 AIH
- - Made reading archives from a resource more robust
-
- 91/04/02 AIH
- - Added functions for reading from and writing to the scrap
-
- 91/03/07 AIH
- - Added some comments and future ideas
-
- 91/01/21 AIH
- - Added some comments describing this file
-
- 91/01/05 Ari Halberstadt (AIH)
- - Inserted this standard header in all files */
-
- #include <string.h>
- #include "ArchiveLib.h"
- #include "MathLib.h"
- #include "MemoryLib.h"
- #include "ResourceLib.h"
-
- /* The arsize and arversion fields of the archive header are set to these
- values. Having version and size fields for the archive header allows for
- future expansion of the information stored in the archive header.
- Currently, both fields are ignored. */
- #define ARCHIVE_VERSION (0)
- #define ARCHIVE_SIZE (0)
-
- /* the header which is written before the archived data */
- typedef struct {
- size_t size; /* size of following data */
- long version; /* version of following data */
- short arsize; /* size of this archive (not used) */
- short arversion; /* version of this archive (not used) */
- } ArchiveHdrType;
-
- /* fill in the fields of the archive's header */
- static void ArchiveSet(ArchiveHdrType *hdr, size_t size, long version)
- {
- require(size >= 0);
- hdr->size = size;
- hdr->version = version;
- hdr->arsize = ARCHIVE_SIZE;
- hdr->arversion = ARCHIVE_VERSION;
- }
-
- /* Seek to the n'th archive entry in the file. The size parameter should
- give the size of the data stored in the archive. This assumes that each
- archive entry is the same size. */
- void ArchiveSeek(FileType *fp, FilePosType n, size_t size)
- {
- require(FileValid(fp));
- require(n >= 0);
- require(size >= 0);
- FileSeek(fp, fsFromStart, n * (size + sizeof(ArchiveHdrType)));
- }
-
- /* First write an ArchiveHdrType, with the size and version fields
- filled in using the given size and version. Then write the data
- to the file. The file must be open for writing. */
- void ArchiveWrite(FileType *fp, const void *data, size_t size, long version)
- {
- ArchiveHdrType hdr;
-
- require(FileValid(fp));
- require(size >= 0);
- ArchiveSet(&hdr, size, version);
- FileWrite(fp, sizeof(ArchiveHdrType), &hdr);
- FileWrite(fp, size, data);
- }
-
- /* Read the archived data from the file. The size parameter should give the
- maximum amount of information which may be copied into the data pointer.
- If more data are available in the archive they are ignored, and if less
- data are available then the extra bytes in the returned data are set to 0.
- The size parameter is set to the actual amount of data returned, and the
- version parameter is set to the version field of the archive. */
- void ArchiveRead(FileType *fp, void *data, size_t *size, long *version)
- {
- ArchiveHdrType hdr;
- FilePosType length = 0;
-
- require(FileValid(fp));
- require(*size >= 0);
- memclr(&hdr, sizeof(ArchiveHdrType));
- (void) FileRead(fp, sizeof(ArchiveHdrType), &hdr);
- length = FileRead(fp, min(hdr.size, *size), data);
- if (hdr.size > *size) /* skip any remaining info */
- FileSeek(fp, fsFromMark, hdr.size - *size);
- if (length < *size) /* clear any extra bytes */
- memclr((char *) data + length, *size - length);
- *size = length; /* return size and version */
- *version = hdr.version;
- }
-
- /* write the data to an archive resource with the given type and ID */
- void ArchiveWriteRes(ResType type, short id,
- const void *data, size_t size, long version)
- {
- ArchiveHdrType hdr;
- Handle archive = NULL;
-
- require(size >= 0);
- TRY {
- ArchiveSet(&hdr, size, version);
- archive = HandleBegin(sizeof(ArchiveHdrType) + size);
- BlockMove(&hdr, *archive, sizeof(ArchiveHdrType));
- BlockMove(data, *archive + sizeof(ArchiveHdrType), size);
- ResSet(archive, type, id);
- } CLEANUP {
- HandleEnd(archive);
- } ENDTRY;
- }
-
- /* same as ArchiveWriteRes, but the data are written from a handle */
- void ArchiveWriteResHandle(ResType type, short id,
- void *data, size_t size, long version)
- {
- SignedByte state = HandleLockHi(data);
- ArchiveWriteRes(type, id, *(Handle) data, size, version);
- HandleRestore(data, state);
- }
-
- /* the same as ArchiveRead but gets the data from a resource */
- void ArchiveReadRes(ResType type, short id,
- void *data, size_t *size, long *version)
- {
- ArchiveHdrType hdr;
- Handle rsrc = NULL;
- size_t length = 0;
-
- require(*size >= 0);
- rsrc = ResGet(type, id);
- if (SizeResource(rsrc) >= sizeof(ArchiveHdrType)) {
- BlockMove(*rsrc, &hdr, sizeof(ArchiveHdrType));
- check(hdr.size == SizeResource(rsrc) - sizeof(ArchiveHdrType));
- length = min(hdr.size, *size);
- BlockMove(*rsrc + sizeof(ArchiveHdrType), data, length);
- }
- if (length < *size) /* clear any extra bytes */
- memclr((char *) data + length, *size - length);
- *size = length; /* return size and version */
- *version = hdr.version;
- }
-
- /* same as ArchiveReadRes, but the data are read into a handle */
- void ArchiveReadResHandle(ResType type, short id,
- void *data, size_t *size, long *version)
- {
- SignedByte state = HandleLockHi(data);
- ArchiveReadRes(type, id, *(Handle) data, size, version);
- HandleRestore(data, state);
- }
-